﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Linq.Expressions;
using Aragorn.SOA.ORM.Lambda;
using Dapper;
using Aragorn.SOA.ORM.Lambda;

namespace Aragorn.SOA.ORM
{
    public class SqlLamCommand<T> : SqlLamCommandBase
    {
        private DbContextData Data
        {
            get;
            set;
        }

        internal SqlLam<T> Query
        {
            get;
            set;
        }

        public SqlLamCommand(DbContextData contextData)
        {
            this.Data = contextData;
            this.Query = new SqlLam<T>();
        }

        public SqlLamCommand(DbContextData contextData, Expression<Func<T, bool>> expression)
        {
            this.Data = contextData;
            this.Query = new SqlLam<T>(expression);
        }

        public SqlLamCommand<T> And(Expression<Func<T, bool>> expression)
        {
            this.Query = this.Query.And(expression);
            return this;
        }

        internal override SqlLamBase GetBaseQuery()
        {
            return this.Query;
        }

        public SqlLamCommand<T> GroupBy(Expression<Func<T, object>> expression)
        {
            this.Query = this.Query.GroupBy(expression);
            return this;
        }

        public SqlLamCommand<T2> Join<T2>(Expression<Func<T, T2, bool>> expression)
        {
            SqlLamCommand<T2> sqlLamCommand = new SqlLamCommand<T2>(this.Data)
            {
                Query = this.Query.Join<T2>(expression)
            };
            return sqlLamCommand;
        }

        public SqlLamCommand<TResult> Join<T2, TKey, TResult>(SqlLamCommand<T2> joinQuery, Expression<Func<T, TKey>> primaryKeySelector, Expression<Func<T, TKey>> foreignKeySelector, Func<T, T2, TResult> selection)
        {
            SqlLamCommand<TResult> sqlLamCommand = new SqlLamCommand<TResult>(this.Data)
            {
                Query = this.Query.Join<T2, TKey, TResult>(joinQuery.Query, primaryKeySelector, foreignKeySelector, selection)
            };
            return sqlLamCommand;
        }

        public SqlLamCommand<T> Or(Expression<Func<T, bool>> expression)
        {
            this.Query = this.Query.Or(expression);
            return this;
        }

        public SqlLamCommand<T> OrderBy(Expression<Func<T, object>> expression)
        {
            this.Query = this.Query.OrderBy(expression);
            return this;
        }

        public SqlLamCommand<T> OrderByDescending(Expression<Func<T, object>> expression)
        {
            this.Query = this.Query.OrderByDescending(expression);
            return this;
        }

        public IEnumerable<dynamic> QueryDynamicMany()
        {
            
            return this.Data.Connection.Query<object>(this.Query.QueryString, this.Data, this.Query.QueryParameters, this.Data.Transaction, true, this.Data.CommandTimeout, null);
        }

        public IEnumerable<dynamic> QueryDynamicPage(int pageIndex, int pageSize, out int recordCount)
        {
            string countSql = DapperExtensions.DapperExtensions.SqlDialect.GetCountSql(this.Query.QueryString);
            
            recordCount = this.Data.Connection.ExecuteScalar<int>(countSql, this.Data, this.Query.QueryParameters, this.Data.Transaction, this.Data.CommandTimeout, null);
            string pagingSql = DapperExtensions.DapperExtensions.SqlDialect.GetPagingSql(this.Query.QueryString, pageIndex, pageSize, this.Query.QueryParameters);
            
            return this.Data.Connection.Query<object>(pagingSql, this.Data, this.Query.QueryParameters, this.Data.Transaction, true, this.Data.CommandTimeout, null);
        }

        public dynamic QueryDynamicSingle()
        {
            IEnumerable<object> objs = this.QueryDynamicMany();
            if (objs == null || objs.Count<object>() == 0)
            {
                return null;
            }
            return objs.FirstOrDefault<object>();
        }

        public IEnumerable<T> QueryMany()
        {
            return this.Data.Connection.Query<T>(this.Query.QueryString, this.Data, this.Query.QueryParameters, this.Data.Transaction, true, this.Data.CommandTimeout, null);
        }

        public IEnumerable<T> QueryPage(int pageIndex, int pageSize, out int recordCount)
        {
            string countSql = DapperExtensions.DapperExtensions.SqlDialect.GetCountSql(this.Query.QueryString);
            //CommandType? nullable = null;
            recordCount = this.Data.Connection.ExecuteScalar<int>(countSql, this.Data, this.Query.QueryParameters, this.Data.Transaction, this.Data.CommandTimeout, null);
            string pagingSql = DapperExtensions.DapperExtensions.SqlDialect.GetPagingSql(this.Query.QueryString, pageIndex, pageSize, this.Query.QueryParameters);
            //CommandType? nullable1 = null;
            return this.Data.Connection.Query<T>(pagingSql, this.Data, this.Query.QueryParameters, this.Data.Transaction, true, this.Data.CommandTimeout, null);
        }

        public T QuerySingle()
        {
            IEnumerable<T> ts = this.QueryMany();
            if (ts != null && ts.Count<T>() != 0)
            {
                return ts.FirstOrDefault<T>();
            }
            return default(T);
        }

        public SqlLamCommand<T> Select(params Expression<Func<T, object>>[] expressions)
        {
            this.Query = this.Query.Select(expressions);
            return this;
        }

        public SqlLamCommand<T> SelectAverage(Expression<Func<T, object>> expression)
        {
            this.Query = this.Query.SelectAverage(expression);
            return this;
        }

        public SqlLamCommand<T> SelectCount(Expression<Func<T, object>> expression)
        {
            this.Query = this.Query.SelectCount(expression);
            return this;
        }

        public SqlLamCommand<T> SelectDistinct(Expression<Func<T, object>> expression)
        {
            this.Query = this.Query.SelectDistinct(expression);
            return this;
        }

        public SqlLamCommand<T> SelectMax(Expression<Func<T, object>> expression)
        {
            this.Query = this.Query.SelectMax(expression);
            return this;
        }

        public SqlLamCommand<T> SelectMin(Expression<Func<T, object>> expression)
        {
            this.Query = this.Query.SelectMin(expression);
            return this;
        }

        public SqlLamCommand<T> SelectSum(Expression<Func<T, object>> expression)
        {
            this.Query = this.Query.SelectSum(expression);
            return this;
        }

        public SqlLamCommand<T> Where(Expression<Func<T, bool>> expression)
        {
            this.Query = this.Query.Where(expression);
            return this;
        }

        public SqlLamCommand<T> WhereIsIn(Expression<Func<T, object>> expression, SqlLamCommandBase sqlQuery)
        {
            this.Query = this.Query.WhereIsIn(expression, sqlQuery.GetBaseQuery());
            return this;
        }

        public SqlLamCommand<T> WhereIsIn(Expression<Func<T, object>> expression, IEnumerable<object> values)
        {
            this.Query = this.Query.WhereIsIn(expression, values);
            return this;
        }

        public SqlLamCommand<T> WhereNotIn(Expression<Func<T, object>> expression, SqlLamCommandBase sqlQuery)
        {
            this.Query = this.Query.WhereNotIn(expression, sqlQuery.GetBaseQuery());
            return this;
        }

        public SqlLamCommand<T> WhereNotIn(Expression<Func<T, object>> expression, IEnumerable<object> values)
        {
            this.Query = this.Query.WhereNotIn(expression, values);
            return this;
        }
    }
}